package tcpsv

import (
	"errors"
	"gitee.com/dreamwood/ez-go/ez"
	"net"
	"sync"
)

type EzTcpServer struct {
	Ip         string
	Port       int
	links      map[string]*net.TCPConn
	channels   map[string]chan []byte
	OnConnect  func(clientIp string)
	OnMessage  func(clientIp string, data []byte)
	OnLost     func(clientIp string) //丢失是异常的关闭，只有在写入失败的时候才会触发
	KeepListen bool
	ReadSize   int
	ChanSize   int
	Wg         sync.WaitGroup
	Lock       sync.Mutex
}

func NewTcpServer(ip string, port int) *EzTcpServer {
	return &EzTcpServer{
		Ip:         ip,
		Port:       port,
		links:      make(map[string]*net.TCPConn),
		channels:   make(map[string]chan []byte),
		KeepListen: true,
		ReadSize:   1024,
		ChanSize:   100,
		Wg:         sync.WaitGroup{},
		Lock:       sync.Mutex{},
	}
}

func (this *EzTcpServer) Run() {
	tcpListener, e := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(this.Ip), Port: this.Port})
	if e != nil {
		ez.LogToConsole(e.Error())
	} else {
		for {
			tcpConn, err := tcpListener.AcceptTCP()
			if err != nil {
				panic(err)
			}
			clientIp := tcpConn.RemoteAddr().String()
			ez.LogToConsole("新的客户端连接到控制端服务进程:" + clientIp)
			this.addConnect(clientIp, tcpConn)
			this.OnConnect(clientIp)
			go this.Listen(clientIp)
		}
	}
}

func (this *EzTcpServer) addConnect(clientIp string, conn *net.TCPConn) {
	this.Lock.Lock()
	defer this.Lock.Unlock()
	this.links[clientIp] = conn
	this.channels[clientIp] = make(chan []byte, this.ChanSize)
}

func (this *EzTcpServer) removeConnect(clientIp string) {
	this.Lock.Lock()
	defer this.Lock.Unlock()
	delete(this.links, clientIp)
	close(this.channels[clientIp])
	delete(this.channels, clientIp)
}

func (this *EzTcpServer) Send(clientIp string, data []byte) (int, error) {
	if this.links[clientIp] != nil {
		n, e := this.links[clientIp].Write(data)
		//如果设备不在线了就删除links
		if e != nil {
			this.removeConnect(clientIp)
			this.OnLost(clientIp)
		}
		return n, e
	} else {
		return 0, errors.New("链接不存在")
	}
}

func (this *EzTcpServer) Listen(clientIp string) {
	for {
		if this.links[clientIp] != nil {
			data := make([]byte, this.ReadSize)
			n, e := this.links[clientIp].Read(data)
			if e != nil {
				this.removeConnect(clientIp)
				this.OnLost(clientIp)
				break
			}
			if n > 0 {
				this.channels[clientIp] <- data[:n]
				this.OnMessage(clientIp, data[:n])
			}
		}
	}
}
